using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Drawing.Imaging;
using System.IO;

namespace HtmlReport
{
    public class BarCodeRender
    {
        private Hashtable _encoding = new Hashtable();
        private const short _wideBarWidth = 2;
        private const short _narrowBarWidth = 1;

        private const short _barHeight = 31;
        public BarCodeRender()
        {
            _encoding.Add("*", "bWbwBwBwb");
            _encoding.Add("-", "bWbwbwBwB");
            _encoding.Add("$", "bWbWbWbwb");
            _encoding.Add("%", "bwbWbWbWb");
            _encoding.Add(" ", "bWBwbwBwb");
            _encoding.Add(".", "BWbwbwBwb");
            _encoding.Add("/", "bWbWbwbWb");
            _encoding.Add("+", "bWbwbWbWb");
            _encoding.Add("0", "bwbWBwBwb");
            _encoding.Add("1", "BwbWbwbwB");
            _encoding.Add("2", "bwBWbwbwB");
            _encoding.Add("3", "BwBWbwbwb");
            _encoding.Add("4", "bwbWBwbwB");
            _encoding.Add("5", "BwbWBwbwb");
            _encoding.Add("6", "bwBWBwbwb");
            _encoding.Add("7", "bwbWbwBwB");
            _encoding.Add("8", "BwbWbwBwb");
            _encoding.Add("9", "bwBWbwBwb");
            _encoding.Add("A", "BwbwbWbwB");
            _encoding.Add("B", "bwBwbWbwB");
            _encoding.Add("C", "BwBwbWbwb");
            _encoding.Add("D", "bwbwBWbwB");
            _encoding.Add("E", "BwbwBWbwb");
            _encoding.Add("F", "bwBwBWbwb");
            _encoding.Add("G", "bwbwbWBwB");
            _encoding.Add("H", "BwbwbWBwb");
            _encoding.Add("I", "bwBwbWBwb");
            _encoding.Add("J", "bwbwBWBwb");
            _encoding.Add("K", "BwbwbwbWB");
            _encoding.Add("L", "bwBwbwbWB");
            _encoding.Add("M", "BwBwbwbWb");
            _encoding.Add("N", "bwbwBwbWB");
            _encoding.Add("O", "BwbwBwbWb");
            _encoding.Add("P", "bwBwBwbWb");
            _encoding.Add("Q", "bwbwbwBWB");
            _encoding.Add("R", "BwbwbwBWb");
            _encoding.Add("S", "bwBwbwBWb");
            _encoding.Add("T", "bwbwBwBWb");
            _encoding.Add("U", "BWbwbwbwB");
            _encoding.Add("V", "bWBwbwbwB");
            _encoding.Add("W", "BWBwbwbwb");
            _encoding.Add("X", "bWbwBwbwB");
            _encoding.Add("Y", "BWbwBwbwb");
            _encoding.Add("Z", "bWBwBwbwb");
        }

        protected System.Drawing.Brush getBCSymbolColor(string symbol)
        {
            System.Drawing.Brush functionReturnValue = null;
            functionReturnValue = Brushes.Black;

            if (symbol == "W" | symbol == "w")
            {
                functionReturnValue = Brushes.White;
            }
            return functionReturnValue;
        }

        //Now we determine whether or not we are going to be drawing a small or large BC bar on this character code
        protected short getBCSymbolWidth(string symbol)
        {
            short functionReturnValue = 0;
            functionReturnValue = _narrowBarWidth;
            if (symbol == "B" | symbol == "W")
            {
                functionReturnValue = _wideBarWidth;
            }
            return functionReturnValue;
        }
        //Find the encoding method in the codec list on the computer based on the known-name (PNG, JPEG, etc)
        protected static ImageCodecInfo FindCodecInfo(string codec)
        {
            ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
            foreach (ImageCodecInfo e in encoders)
            {
                if (e.FormatDescription.Equals(codec))
                    return e;
            }
            return null;
        }

        //Now the fun part, this function is called to generate the actual barcode
        protected virtual Bitmap GenerateBarcodeImage(short imageWidth, short imageHeight, string Code)
        {
            //Declare a new bitmap canvas to store our new barcode (well, it will be new -- we will make it soon)!
            Bitmap b = new Bitmap(imageWidth, imageHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            //Create the actualy canvas associated with the barcode drawing       
            Rectangle canvas = new Rectangle(0, 0, imageWidth, imageHeight);

            //Create our graphics object from our barcode canvas
            Graphics g = Graphics.FromImage(b);

            //Fill the drawing with a white background
            g.FillRectangle(Brushes.White, 0, 0, imageWidth, imageHeight);

            //Draw the "eye candy" items on the barcode canvas
            //Here we are drawing the gradient directly behind the barcode are at the top
            g.FillRectangle(new System.Drawing.Drawing2D.LinearGradientBrush(new System.Drawing.RectangleF(1, 1, 169, 30), Color.White, Color.LightGray, System.Drawing.Drawing2D.LinearGradientMode.Vertical), 1, 1, 169, 30);

            //Now we draw the seperation line (under barcode) and the liner gradient background
            g.FillRectangle(new System.Drawing.Drawing2D.LinearGradientBrush(new System.Drawing.RectangleF(1, 34, 169, 15), Color.LightGray, Color.White, System.Drawing.Drawing2D.LinearGradientMode.Vertical), 1, 34, 169, 15);
            g.FillRectangle(new SolidBrush(Color.Black), 1, 33, 169, 1);

            //Now that we have the "fine art" drawn, let's switch over to high-quality rendering for our text and images
            //However, we are using SingleBitPerPixel because when printed it appears sharper as opposed to anti-aliased
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;


            //Write out the original barcode ID
            g.DrawString(Code, new Font("Tahoma", 8, FontStyle.Bold), new SolidBrush(Color.Black), 3, 34);

            //Write out a lighter barcode generation script version on the right (again, eye candy)
            //g.DrawString("ITS v2.0", new Font("Tahoma", 8, FontStyle.Bold), new SolidBrush(Color.Gray), 113, 34);

            //Now that we are done with the high quality rendering, we are going to draw the barcode lines -- which needs to be very straight, and not blended
            //Else it may blur and cause complications with the barcode reading device -- so we won't take any chances.
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.Default;

            //Code has to be surrounded by asterisks to make it a valid C39 barcode, so add "*" to the front and read of the barcode
            string UseCode = string.Format("{0}{1}{0}", "*", Code);

            //Define a starting X and Y position for the barcode
            short XPosition = 1;
            short YPosition = 1;

            //Initialize our IC marker, and give a default of false
            //This is used to track what?  Incorrectly assigned characters for the barcode (ones that won't match
            //C39 standards) So we don't use them, and mark it as invalid.
            bool invalidCharacter = false;

            //Declare our current character string holding variable
            string CurrentSymbol = string.Empty;

            //THIS PART IS *ONLY* FOR CALCULATING THE WIDTH OF THE BARCODE TO CENTER IT!
            //Begin at the starting area of our FINAL rendered barcode value
            for (short j = 0; j <= Convert.ToInt16(UseCode.Length - 1); j++)
            {
                //Set our current character to the character space of the barcode
                CurrentSymbol = UseCode.Substring(j, 1);

                //Check to ensure that it's a valid character per our encoding hashtable we defined earlier
                if ((_encoding[CurrentSymbol] != null))
                {
                    //Create a new rendered version of the character per our hashtable with valid values (don't read it, it will be encoded -- look above at the HT)
                    string EncodedSymbol = _encoding[CurrentSymbol].ToString();

                    //Progress throughout the entire encoding value of this character
                    for (short i = 0; i <= Convert.ToInt16(EncodedSymbol.Length - 1); i++)
                    {
                        //Extract the current encoded character value from the complete rendering of this character (it's getting deep, right?)
                        string CurrentCode = EncodedSymbol.Substring(i, 1);

                        //Change our coordinates for drawing to match the next position (current position plus the char. bar width)
                        XPosition = (short)(XPosition + getBCSymbolWidth(CurrentCode));
                    }

                    //Now we need to "create" a whitespace as needed, and get the width
                    XPosition = XPosition = (short)(XPosition + (short)getBCSymbolWidth("w"));
                }
            }

            //Now the nice trick of division helps with centering the barcode on the canvas
            XPosition = XPosition = (short)((imageWidth / 2) - (XPosition / 2));

            //NOW WE GO LIVE!  THIS IS WHERE WE ACTUALLY DRAW THE BARCODE BARS
            //Begin at the starting area of our FINAL rendered barcode value
            for (short j = 0; j <= Convert.ToInt16(UseCode.Length - 1); j++)
            {
                //Set our current character to the character space of the barcode
                CurrentSymbol = UseCode.Substring(j, 1);

                //Check to ensure that it's a valid character per our encoding hashtable we defined earlier                  
                if ((_encoding[CurrentSymbol] != null))
                {
                    //Create a new rendered version of the character per our hashtable with valid values (don't read it, it will be encoded -- look above at the HT)
                    string EncodedSymbol = _encoding[CurrentSymbol].ToString();

                    //Progress throughout the entire encoding value of this character
                    for (short i = 0; i <= Convert.ToInt16(EncodedSymbol.Length - 1); i++)
                    {
                        //Extract the current encoded character value from the complete rendering of this character (it's getting deep, right?)
                        string CurrentCode = EncodedSymbol.Substring(i, 1);

                        //Use our drawing graphics object on the canvase to create a bar with out position and values based on the current character encoding value
                        g.FillRectangle(getBCSymbolColor(CurrentCode), XPosition, YPosition, getBCSymbolWidth(CurrentCode), _barHeight);
                        //Lets disect this a little to see how it actually works, want to?
                        //   getBCSymbolColor(CurrentCode)
                        //       We already know, but this gets the color of the bar, either white or colorized (in this case, black)
                        //   XPosition, YPosition
                        //       Again, we already know -- but this is the coordinates to draw the bar based on previous locations
                        //   getBCSymbolWidth(CurrentCode)
                        //       This is the important part, we get the correct width (either narrow or wide) for this character (post encoding)
                        //   _barHeight
                        //       This is static as defined earlier, it doesn't much matter but it also depends on your Barcode reader

                        //Change our coordinates for drawing to match the next position (current position plus the char. bar width)
                        XPosition = XPosition = (short)(XPosition + getBCSymbolWidth(CurrentCode));
                    }

                    //Now we need to "ACTUALLY" create a whitespace as needed, and get the width
                    g.FillRectangle(getBCSymbolColor("w"), XPosition, YPosition, getBCSymbolWidth("w"), _barHeight);

                    //Change our coordinates for drawing to match the next position (current position plus the char. bar width)
                    XPosition = XPosition = (short)(XPosition + getBCSymbolWidth("w"));
                }
                else
                {
                    //This is our fallback, if it's not a valid character per our hashtable in C39, discard!
                    invalidCharacter = true;
                }
            }

            //As we set it above (if needed) for an invalid character (not allowed by the C39 guide), then we handle it here  
            if (invalidCharacter)
            {
                //Just fill the whole canvas white
                g.FillRectangle(Brushes.White, 0, 0, imageWidth, imageHeight);

                //What's the deal?  Tell them!  It's not right, so we can't do it -- here is why.
                g.DrawString("Invalid Charachers Detected", new Font("Tahoma", 8), new SolidBrush(Color.Red), 0, 0);
                g.DrawString("- Barcode Not Generated -", new Font("Tahoma", 8), new SolidBrush(Color.Black), 0, 10);
                g.DrawString(Code, new Font("Tahoma", 8, FontStyle.Italic), new SolidBrush(Color.Black), 0, 30);
            }


            //Clean-up!  Nobody likes a possible memory leaking application!
            g.Dispose();

            //Finally, return the image via the memorystream
            return b;
        }

        public static void WriteBarcode(string Code, int Width,int Height,string FileName)
        {
            BarCodeRender render = new BarCodeRender();
            Bitmap image = render.GenerateBarcodeImage((short)Width, (short)Height, Code);

            string ext = System.IO.Path.GetExtension(FileName).ToLower();
            string des = "BMP";
            switch (ext)
            {
                case ".jpg":
                    des = "JPEG";
                    break;
                case ".gif":
                    des = "GIF";
                    break;
                case ".png":
                    des = "PNG";
                    break;
                case ".tif":
                    des = "TIF";
                    break;
            }

            //Setup the encoding quality of the final barcode rendered image
            EncoderParameters encodingParams = new EncoderParameters();
            encodingParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100);

            //Define the encoding details of "how" for the image
            //We will use PNG because, well it's got the best image quality for it's footprint
            ImageCodecInfo encodingInfo = FindCodecInfo(des);
            if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(FileName)))
            {
                System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(FileName));
            }
            image.Save(FileName, encodingInfo, encodingParams);
            image.Dispose();
        }
    }
}
